setfenv(1, require'winapi')
require'winapi.window'
require'winapi.router'
require'winapi.comctl'

--creation

WC_BUTTON            = 'Button'

BS_PUSHBUTTON        = 0x00000000
BS_DEFPUSHBUTTON     = 0x00000001
BS_CHECKBOX          = 0x00000002
BS_AUTOCHECKBOX      = 0x00000003
BS_RADIOBUTTON       = 0x00000004
BS_3STATE            = 0x00000005
BS_AUTO3STATE        = 0x00000006
BS_GROUPBOX          = 0x00000007
BS_USERBUTTON        = 0x00000008
BS_AUTORADIOBUTTON   = 0x00000009
BS_PUSHBOX           = 0x0000000A
BS_OWNERDRAW         = 0x0000000B
BS_TYPEMASK          = 0x0000000F
BS_LEFTTEXT          = 0x00000020
BS_TEXT              = 0x00000000
BS_ICON              = 0x00000040
BS_BITMAP            = 0x00000080
BS_LEFT              = 0x00000100
BS_RIGHT             = 0x00000200
BS_CENTER            = 0x00000300
BS_TOP               = 0x00000400
BS_BOTTOM            = 0x00000800
BS_VCENTER           = 0x00000C00
BS_PUSHLIKE          = 0x00001000
BS_MULTILINE         = 0x00002000
BS_NOTIFY            = 0x00004000
BS_FLAT              = 0x00008000
BS_RIGHTBUTTON       = BS_LEFTTEXT
BS_SPLITBUTTON       = 0x0000000C
BS_DEFSPLITBUTTON    = 0x0000000D
BS_COMMANDLINK       = 0x0000000E
BS_DEFCOMMANDLINK    = 0x0000000F


BUTTON_DEFAULTS = {
	class = WC_BUTTON,
	caption = '&OK',
	style = bit.bor(WS_CHILD, WS_VISIBLE, WS_TABSTOP, BS_NOTIFY),
	x = 10, y = 10, w = 100, h = 24,
}

function CreateButton(info)
	info = update({}, BUTTON_DEFAULTS, info)
	return CreateWindow(info)
end

local function variation(defaults)
	return function(info)
		return CreateButton(update({}, defaults, info))
	end
end

CreatePushButton = CreateButton

CreateCheckBox = variation{
	caption = 'Unnamed option',
	style = bit.bor(BUTTON_DEFAULTS.style, BS_AUTOCHECKBOX),
}

CreateRadioButton = variation{
	caption = 'Unnamed option',
	style = bit.bor(BUTTON_DEFAULTS.style, BS_AUTORADIOBUTTON),
}

CreateGroupBox = variation{
	caption = 'Group',
	style = bit.bor(BUTTON_DEFAULTS.style, BS_GROUPBOX),
}

-- SPLIT BUTTON INFO mask flags
BCSIF_GLYPH             = 0x0001
BCSIF_IMAGE             = 0x0002
BCSIF_STYLE             = 0x0004
BCSIF_SIZE              = 0x0008

-- SPLIT BUTTON STYLE flags
BCSS_NOSPLIT            = 0x0001
BCSS_STRETCH            = 0x0002
BCSS_ALIGNLEFT          = 0x0004
BCSS_IMAGE              = 0x0008

CreateSplitButton = variation{
	caption = 'Options...',
	style = bit.bor(BUTTON_DEFAULTS.style, BS_SPLITBUTTON),
}

--commands

BM_GETCHECK         = 0x00F0
BM_SETCHECK         = 0x00F1
BM_GETSTATE         = 0x00F2
BM_SETSTATE         = 0x00F3
BM_SETSTYLE         = 0x00F4
BM_CLICK            = 0x00F5
BM_GETIMAGE         = 0x00F6
BM_SETIMAGE         = 0x00F7

Button_Enable = EnableWindow
Button_GetText = GetWindowText
Button_SetText = SetWindowText

function Button_GetCheck(hwndCtl)
	return SNDMSG(hwndCtl, BM_GETCHECK) ~= 0
end

function Button_SetCheck(hwndCtl, check)
	return SNDMSG(hwndCtl, BM_SETCHECK, check)
end

BST_UNCHECKED       = 0x0000
BST_CHECKED         = 0x0001
BST_INDETERMINATE   = 0x0002
BST_PUSHED          = 0x0004
BST_FOCUS           = 0x0008

function Button_GetState(hwndCtl)
	return SNDMSG(hwndCtl, BM_GETSTATE)
end

function Button_SetState(hwndCtl, state)
	checknz(SNDMSG(hwndCtl, BM_SETSTATE, state))
end

function Button_SetStyle(hwndCtl, style, fRedraw)
	checknz(SNDMSG(hwndCtl, BM_SETSTYLE, style, MAKELPARAM(fRedraw and 1 or 0, 0)))
end

BCM_FIRST               = 0x1600
BCM_GETIDEALSIZE        = (BCM_FIRST + 0x0001)
BCM_SETIMAGELIST        = (BCM_FIRST + 0x0002)
BCM_GETIMAGELIST        = (BCM_FIRST + 0x0003)
BCM_SETTEXTMARGIN       = (BCM_FIRST + 0x0004)
BCM_GETTEXTMARGIN       = (BCM_FIRST + 0x0005)
BCM_SETDROPDOWNSTATE    = (BCM_FIRST + 0x0006)
BCM_SETSPLITINFO        = (BCM_FIRST + 0x0007)
BCM_GETSPLITINFO        = (BCM_FIRST + 0x0008)
BCM_SETNOTE             = (BCM_FIRST + 0x0009)
BCM_GETNOTE             = (BCM_FIRST + 0x000A)
BCM_GETNOTELENGTH       = (BCM_FIRST + 0x000B)
BCM_SETSHIELD           = (BCM_FIRST + 0x000C)

function Button_GetIdealSize(hwnd, size)
	size = types.SIZE(size)
	checknz(SNDMSG(hwnd, BCM_GETIDEALSIZE, 0, ffi.cast('PSIZE', size)))
	return size
end

BUTTON_IMAGELIST_ALIGN_LEFT     = 0
BUTTON_IMAGELIST_ALIGN_RIGHT    = 1
BUTTON_IMAGELIST_ALIGN_TOP      = 2
BUTTON_IMAGELIST_ALIGN_BOTTOM   = 3
BUTTON_IMAGELIST_ALIGN_CENTER   = 4       -- Doesn't draw text

ffi.cdef[[
enum PUSHBUTTONSTATES {
	PBS_NORMAL = 1,
	PBS_HOT = 2,
	PBS_PRESSED = 3,
	PBS_DISABLED = 4,
	PBS_DEFAULTED = 5,
	PBS_DEFAULTED_ANIMATING = 6,
};

typedef struct
{
    HIMAGELIST  himl;
    RECT        _margin;
    UINT        uAlign;
} BUTTON_IMAGELIST, *PBUTTON_IMAGELIST;
]]

BUTTON_IMAGELIST = struct{
	ctype = 'BUTTON_IMAGELIST',
	fields = sfields{
		'imagelist', 'himl', pass,
		'margin', '_margin', types.RECT,
		'align', 'uAlign', flags,
	}
}

function Button_SetImageList(hwnd, bimlist)
   bimlist = BUTTON_IMAGELIST(bimlist)
	checknz(SNDMSG(hwnd, BCM_SETIMAGELIST, 0, ffi.cast('PBUTTON_IMAGELIST', bimlist)))
end

function Button_GetImageList(hwnd, pbimlist)
	local pbimlist = types.PBUTTON_IMAGELIST(pbimlist)
   checknz(SNDMSG(hwnd, BCM_GETIMAGELIST, 0, pbimagelist))
	return pbimagelist
end

function Button_SetTextMargin(hwnd, pmargin)
	checknz(SNDMSG(hwnd, BCM_SETTEXTMARGIN, 0, pmargin))
end

function Button_GetTextMargin(hwnd)
   --pmargin
	checknz(SNDMSG(hwnd, BCM_GETTEXTMARGIN, 0, pmargin))
end

function Button_SetDropDownState(hwnd, fDropDown)
   checknz(SNDMSG(hwnd, BCM_SETDROPDOWNSTATE, fDropDown))
end

function Button_SetSplitInfo(hwnd, pInfo)
   checknz(SNDMSG(hwnd, BCM_SETSPLITINFO, 0, pInfo))
end

function Button_GetSplitInfo(hwnd, pInfo)
   checknz(SNDMSG(hwnd, BCM_GETSPLITINFO, 0, pInfo))
end

function Button_SetNote(hwnd, psz)
   checknz(SNDMSG(hwnd, BCM_SETNOTE, 0, ffi.cast('LPCWSTR', wcs(psz))))
end

function Button_GetNote(hwnd, psz, pcc)
   if not psz then
		ppc = checkpoz(SNDMSG(hwnd, BCM_GETNOTELENGTH))
		psz = WCS(pcc)
	else
		ppc = 1
	end
	checknz(SNDMSG(hwnd, BCM_GETNOTE, pcc, ffi.cast('LPCWSTR', psz)))
	return psz
end

-- Macro to use on a button or command link to display an elevated icon
function Button_SetElevationRequiredState(hwnd, fRequired)
    return checkpoz(SNDMSG(hwnd, BCM_SETSHIELD, 0, fRequired))
end

--notifications

BN_CLICKED           = 0
BN_PAINT             = 1
BN_HILITE            = 2
BN_UNHILITE          = 3
BN_DISABLE           = 4
BN_DOUBLECLICKED     = 5
BN_PUSHED            = BN_HILITE
BN_UNPUSHED          = BN_UNHILITE
BN_DBLCLK            = BN_DOUBLECLICKED
BN_SETFOCUS          = 6
BN_KILLFOCUS         = 7

-- NOTIFICATION MESSAGES
ffi.cdef[[
typedef struct tagNMBCDROPDOWN
{
    NMHDR   hdr;
    RECT    rcButton;
} NMBCDROPDOWN, *LPNMBCDROPDOWN;
]]

BCN_FIRST            = ffi.cast('UINT', -1250)
BCN_HOTITEMCHANGE    = (BCN_FIRST + 0x0001)
BCN_DROPDOWN         = (BCN_FIRST + 0x0002)

--showcase

if not ... then
require'winapi.showcase'

local window, router = ShowcaseWindow()
local imagelist = ShowcaseImageList()

local gb = CreateGroupBox{w = 500, h = 240, parent = window}

local b = CreatePushButton{x = 20, y = 200, parent = window, caption = 'That\'s it, enjoy!'}
local size = Button_GetIdealSize(b)
Button_SetImageList(b, {imagelist = imagelist})
SetWindowPos(b, nil, 0, 0, size.cx + 20, size.cy, 'SWP_NOMOVE|SWP_NOZORDER|SWP_NOACTIVATE')

local cb = CreateCheckBox{x = 20, y = 100, w = 300, parent = window, caption = "I'm a cheeckbox, and I'm ok"}
Button_SetCheck(cb, true)

local rb1 = CreateRadioButton{x = 20, y = 50, w = 300, parent = window, caption = "I'm a radiobutton, and I'm ok"}
local rb2 = CreateRadioButton{x = 20, y = 70, w = 300, parent = window, caption = "I sleep all night and I work all day"}
Button_SetCheck(rb2, true)

local function inspect_event(WM,code,lParam,wParam)
	print(findname('WM_', WM), findname('BN_',code))
end

router:set_handler('Control', gb, inspect_event)
router:set_handler('Control', b, inspect_event)
router:set_handler('Control', cb, inspect_event)
router:set_handler('Control', rb1, inspect_event)
router:set_handler('Control', rb2, inspect_event)

MessageLoop()
end

--[[

-- Hot item change flags
#define HICF_OTHER          0x00000000
#define HICF_MOUSE          0x00000001          -- Triggered by mouse
#define HICF_ARROWKEYS      0x00000002          -- Triggered by arrow keys
#define HICF_ACCELERATOR    0x00000004          -- Triggered by accelerator
#define HICF_DUPACCEL       0x00000008          -- This accelerator is not unique
#define HICF_ENTERING       0x00000010          -- idOld is invalid
#define HICF_LEAVING        0x00000020          -- idNew is invalid
#define HICF_RESELECT       0x00000040          -- hot item reselected
#define HICF_LMOUSE         0x00000080          -- left mouse button selected
#define HICF_TOGGLEDROPDOWN 0x00000100          -- Toggle button's dropdown state

typedef struct tagNMBCHOTITEM
{
    NMHDR   hdr;
    DWORD   dwFlags;           -- HICF_*
} NMBCHOTITEM, *LPNMBCHOTITEM;


#define BST_HOT            0x0200

-- BUTTON STATE FLAGS
#define BST_DROPDOWNPUSHED      0x0400

-- BUTTON STRUCTURES
typedef struct tagBUTTON_SPLITINFO
{
    UINT        mask;
    HIMAGELIST  himlGlyph;         -- interpreted as WCHAR if BCSIF_GLYPH is set
    UINT        uSplitStyle;
    SIZE        size;
} BUTTON_SPLITINFO, *PBUTTON_SPLITINFO;

-- Value to pass to BCM_SETIMAGELIST to indicate that no glyph should be
-- displayed
#define BCCL_NOGLYPH  (HIMAGELIST)(-1)

]]
